package site.kuangzhang.probe;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.util.IOUtils;
import org.apache.http.client.ClientProtocolException;
import site.kuangzhang.probe.handler.HandlerFacotry;
import site.kuangzhang.probe.pojo.Miner;
import site.kuangzhang.probe.util.Http;
import site.kuangzhang.probe.util.IOUtil;
import site.kuangzhang.probe.util.StaticValue;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * 探针主程序
 */
public class Main {

    private static final String CONFIG_API = "https://api.kuangzhang.site/kuangzhang/ProbeApi/config";
    private static final String PROBE_API = "https://api.kuangzhang.site/kuangzhang/ProbeApi/execute";

    private static String token = null;

    private static volatile long timeStamp = System.currentTimeMillis();

    private static ConcurrentHashMap<String, Long> restartMap = new ConcurrentHashMap<>();

    public static void main(String[] args) {
        if (!valite()) {
            System.err.println("发生异常30秒后停止。。。。。。。。");
            try {
                Thread.sleep(30000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return;
        }

        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1200000L);
                System.out.println("如果你看到这句话说明采集程序遇到了缓慢地址,请联系我们");
                Thread.sleep(30000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //程序超时自动杀死
                System.exit(1);
            }
        });
        thread.setDaemon(true);
        thread.start(); ;

        execute();
    }

    private static void execute() {


        ExecutorService threadPool = null;

        try {

            //http 获取远程 配置
            Map<String, Object> params = new HashMap<>();
            params.put("token", token);
            String configResult = null;
            try {
                configResult = Http.sendGet(CONFIG_API, params);
            } catch (IOException e) {
                System.err.println("远程配置获取失败: " + e.getMessage());
            }

            if (configResult == null || configResult.length() == 0 || !configResult.startsWith("{")) {
                System.err.println("远程配置获取失败");
                return;
            }

            JSONObject result = JSONObject.parseObject(configResult);

            if (!result.getBooleanValue("ok")) {
                System.err.println("远程配置获取失败: " + result.getString("message"));
                System.err.println("服务发生异常30秒后重试。。。。。。。。");
                return;

            }

            JSONArray ips = result.getJSONArray("obj");

            List<Miner> miners = ips.stream().map(ip -> new Miner((String) ip)).collect(Collectors.toList());

            if (miners.size() > 1000) {
                System.err.println("矿机数目过多。建议分开矿场这样能保证监控效率");
            }

            final CountDownLatch latch = new CountDownLatch(miners.size());
            threadPool = Executors.newFixedThreadPool(Math.min(30, miners.size()));

            for (Miner miner : miners) {
                threadPool.execute(() -> {
                    try {
                        HandlerFacotry.create(miner).execute();

                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        latch.countDown();
                    }
                });
            }

            latch.await(20,TimeUnit.MINUTES) ;

            if(latch.getCount()!=0){
                System.out.println("程序超时，停止运行");
                System.exit(1);
            }

            List<Miner> post = new ArrayList<>();

            int unknowCount = (int) miners.stream().filter(m -> {
                if (HandlerFacotry.UNKNOW.equals(m.getType())) {
                    return true;
                } else {
                    post.add(m);
                    return false;
                }
            }).count();

            Map<String, Object> postParams = new HashMap<>();
            postParams.put("token", token);
            postParams.put("minerJsonStr", JSONArray.toJSONString(post));

            String resultContent = Http.sendPost(PROBE_API, postParams);

            if (resultContent == null || !resultContent.startsWith("{")) {
                throw new Exception("提交失败两分钟后重试.");
            }

            JSONObject upResult = JSONObject.parseObject(resultContent);

            if (upResult.getBooleanValue("ok")) {
                System.out.println(upResult.getString("message"));

                JSONArray restartSet = upResult.getJSONArray("obj");
                //获取需要重器的机器

                if (restartSet != null && restartSet.size() > 0) {
                    for (Object o : restartSet) {
                        String mac = (String) o;
                        Long time = restartMap.getOrDefault(mac, 0L);

                        if (System.currentTimeMillis() - time < 60 * 60000L) {
                            System.out.println("服务器要求重新启动被拒绝，距离上次重新启动不足1个小时");
                            continue;
                        }

                        try {
                            Miner miner = miners.stream().filter(m -> mac.equals(m.getMacAddress())).findFirst().get();
                            Http.getByProvider("http://" + miner.getIp() + "/cgi-bin/reboot.cgi?_=" + System.currentTimeMillis(), "root", "root");
                            System.out.println(miner.getIp() + " 重启成功");
                            restartMap.put(miner.getMacAddress(), System.currentTimeMillis());
                        } catch (Exception e) {
                            System.err.println(mac + " 重启失败，失败信息：" + e.getMessage());
                        }
                    }
                }

            } else {
                System.out.println("提交失败：" + upResult.getString("message"));
            }

            System.out.println("本轮监控完毕,计划矿工：" + miners.size() + "名，实际发现" + (miners.size() - unknowCount) + "名，无法连接" + unknowCount + "名");


        } catch (InterruptedException e) {
            System.out.println("线程被意外终止");
        } catch (ClientProtocolException e) {
            System.out.println("网络连接失败");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("系统发生错误: " + e.getMessage());
        } finally {
            if (threadPool != null) {
                threadPool.shutdown();
            }
            try {
                Thread.sleep(120000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


    }

    private static boolean valite() {

        File config = new File("config.txt");

        if(!config.exists() || !config.canRead() || config.isDirectory()){
            System.out.println("配置文件有误，不存在或者不能读取 路径 ："+config.getAbsolutePath());
            return false;
        }

        String content = IOUtil.getContent(config,"utf-8") ;

        if(content==null || content.trim().length()==0){
            System.out.println("配置文件有误,内容为空") ;
            return false ;
        }


        //矿场token ， 矿机类型 显卡，还是asic
        for (String param : content.split("\n")) {
            param = trim(param) ;
            if (param == null || param.trim().length() == 0 || param.startsWith("#")) {
                continue;
            }

            int index = param.indexOf("=");
            if (index == -1) {
                System.err.println("无效参数 " + param);
                continue;
            }
            String key = trim(param.substring(0, index));
            String value = trim(param.substring(index + 1));

            switch (key) {
                case "token":
                    token = value.replace("[", "").replace("]", "");
                    break;
                default:
                    System.err.println("无效参数 " + param);
            }
        }

        if (token == null || token.trim().length() == 0) {
            System.err.print("token 未填写");
            return false;
        }

        return true;
    }

    /**
     * trim 一个字符串.扩展了string类原生的trim.对BOM和中文空格进行trim
     *
     * @return
     */
    public static String trim(String value) {

        if (value == null) {
            return value;
        }

        int len = value.length();

        int st = 0;

        while ((st < len) && (Character.isWhitespace(value.charAt(st)) || value.charAt(st) == 65279 || value.charAt(st) == 160 || value.charAt(st) == 12288)) {
            st++;
        }
        while ((st < len) && (Character.isWhitespace(value.charAt(len - 1)) || value.charAt(st) == 160 || value.charAt(st) == 12288)) {
            len--;
        }
        return ((st > 0) || (len < value.length())) ? value.substring(st, len) : value;
    }

}
